<template>
  <div class="tree-node">
    <details
      v-if="itemNode.type === 'dir'"
      class="name"
      :title="itemNode.path"
      :open="open"
      @toggle="(e: any) => open = e.target.open"
      @keydown.enter.prevent>
      <summary
        :class="{folder: true, 'folder-selected': selected}"
        :style="`padding-left: ${itemNode.level}em`"
        @contextmenu.exact.prevent.stop="showContextMenu(itemNode)">
        <div class="item">
          <div class="item-label">
            {{ itemNode.name === '/' ? currentRepoName : itemNode.name }} <span class="count">({{itemNode.children ? itemNode.children.length : 0}})</span>
          </div>
          <div class="item-action">
            <svg-icon class="icon" name="folder-plus-solid" @click.exact.stop.prevent="createFolder()" :title="$t('tree.context-menu.create-dir')"></svg-icon>
            <svg-icon class="icon" name="plus" @click.exact.stop.prevent="createFile()" :title="$t('tree.context-menu.create-doc')"></svg-icon>
          </div>
        </div>
      </summary>
      <template v-if="open">
        <tree-node v-for="x in (itemNode.children || [])" :key="x.path" :item="x" />
      </template>
    </details>
    <div
      ref="refFile"
      v-else
      :class="{name: true, 'file-name': true, selected}"
      :style="`padding-left: ${itemNode.level}em`"
      :title="itemNode.path + '\n\n' + fileTitle"
      @click.exact.prevent="select(item)"
      @contextmenu.exact.prevent.stop="showContextMenu(itemNode)">
      <div :class="{'item-label': true, marked, 'type-md': itemNode.name.endsWith('.md')}"> {{ itemNode.name }} </div>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, nextTick, PropType, ref, toRefs, watch } from 'vue'
import { useStore } from 'vuex'
import { useContextMenu } from '@fe/support/ui/context-menu'
import extensions from '@fe/others/file-extensions'
import { triggerHook } from '@fe/core/hook'
import { getContextMenuItems } from '@fe/services/tree'
import type { Components } from '@fe/types'
import { createDir, createDoc, isMarked, openInOS, switchDoc } from '@fe/services/document'
import SvgIcon from './SvgIcon.vue'
import { useI18n } from '@fe/services/i18n'

export default defineComponent({
  name: 'tree-node',
  components: { SvgIcon },
  props: {
    item: {
      type: Object as PropType<Components.Tree.Node>,
      required: true,
    }
  },
  setup (props) {
    const { t } = useI18n()

    const store = useStore()

    const refFile = ref<any>(null)
    const localMarked = ref<boolean | null>(null)

    const itemNode = computed(() => ({ ...props.item, marked: props.item.type === 'file' && isMarked(props.item) }))
    const open = ref(itemNode.value.path === '/')

    watch(() => props.item, () => {
      localMarked.value = null
    })

    const { currentFile, currentRepo } = toRefs(store.state)

    async function createFile () {
      await createDoc({ repo: props.item.repo }, props.item)
    }

    async function createFolder () {
      await createDir({ repo: props.item.repo }, props.item)
    }

    async function select (node: Components.Tree.Node) {
      if (node.type !== 'dir') {
        if (extensions.supported(node.name)) {
          switchDoc(node)
        } else {
          if (!(await triggerHook('TREE_NODE_SELECT', { node }, { breakable: true }))) {
            openInOS(node)
          }
        }
      }
    }

    function showContextMenu (item: any) {
      useContextMenu().show([...getContextMenuItems(item, { localMarked })])
    }

    const currentRepoName = computed(() => currentRepo.value?.name ?? '/')

    const selected = computed(() => {
      if (!currentFile.value) {
        return false
      }

      if (itemNode.value.type === 'dir') {
        return currentFile.value.repo === itemNode.value.repo && currentFile.value.path.startsWith(itemNode.value.path + '/')
      }

      return currentFile.value.repo === itemNode.value.repo && currentFile.value.path === itemNode.value.path
    })

    const shouldOpen = computed(() => {
      return itemNode.value.type === 'dir' && currentFile.value && currentFile.value.path.startsWith(itemNode.value.path + '/') && currentFile.value.repo === itemNode.value.repo
    })

    const marked = computed(() => localMarked.value ?? itemNode.value.marked)

    watch(selected, val => {
      if (val && itemNode.value.type === 'file') {
        nextTick(() => {
          refFile.value.scrollIntoViewIfNeeded()
        })
      }
    }, { immediate: true })

    watch(shouldOpen, val => {
      if (val) {
        nextTick(() => {
          open.value = true
        })
      }
    }, { immediate: true })

    const fileTitle = computed(() => [
      t('tree.created-at', itemNode.value.birthtime ? new Date(itemNode.value.birthtime).toLocaleString() : '-'),
      t('tree.updated-at', itemNode.value.mtime ? new Date(itemNode.value.mtime).toLocaleString() : '-'),
    ].join('\n'))

    return {
      open,
      itemNode,
      refFile,
      fileTitle,
      currentRepoName,
      selected,
      marked,
      showContextMenu,
      select,
      createFile,
      createFolder,
    }
  },
})
</script>

<style scoped>
.tree-node {
  font-size: 15px;
  line-height: 26px;
  cursor: default;
  color: var(--g-color-5);
}

.tree-node * {
  user-select: none;
}

summary {
  outline: none;
}

summary.folder::-webkit-details-marker,
summary.folder::marker {
  flex: none;
  width: 10px;
  margin: 0;
  margin-right: 5px;
}

.folder {
  align-items: center;
}

.folder:hover {
  background: var(--g-color-90);
}

.folder-selected {
  background: var(--g-color-95)
}

.item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

summary > .item {
  display: inline-flex;
  width: calc(100% - 20px);
}

.item-label {
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-all;
  height: 26px;
}

.item-action {
  display: none;
  align-content: center;
  justify-content: space-around;
  flex: none;
}

.item-action .icon {
  padding: 4px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  height: 20px;
  width: 20px;
  border-radius: 50%;
  color: var(--g-color-45);
}

.item-action .icon:hover {
  background: var(--g-color-70);
  color: var(--g-color-25);
}

.item:hover .item-action {
  display: flex;
}

.item .count {
  color: var(--g-color-30);
  font-size: 12px;
  vertical-align: bottom;

  opacity: 0;
}

.item:hover .count {
  opacity: 1;
}

.name {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.file-name {
  padding-left: 0.2em;
  transition: 50ms ease;
  border-left: 4px solid transparent;
}

.file-name.selected {
  background: var(--g-color-85);
  border-left-color: var(--g-color-60);
}

.file-name:hover {
  background: var(--g-color-85);
}

.file-name .item-label::before {
  display: inline-block;
  width: 12px;
  height: 24px;
  margin-right: 4px;
  content: url();
  vertical-align: middle;
}

.file-name .item-label.type-md::before {
  content: url();
}

.file-name .item-label:active {
  transform: translateX(2px);
}

.marked {
  color: #569bd5;
}

.name {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
</style>
